#ifndef _UI_LISTWIDGET_H_
#define _UI_LISTWIDGET_H_

#include "ui/ui_widget.h"

namespace ui
{

class ListItem;
class ListData;

class UIListWidget : public UIWidget
{
public:
	enum LIST_TYPE
	{
		LT_TABLE = 0,						// is a table with rows and columns, predominantly text
		LT_GRID,							// NOT TESTED // is an x*y grid of icons, with optional text
		LT_COUNT,
	};

	enum LIST_PROPERTIES
	{
		LP_CANVSCROLL		= 0x00000001,	// enable vertical scrolling
		LP_CANHSCROLL		= 0x00000002,	// NOT TESTED // enable horizontal scrolling (LT_TABLE only)
		LP_COLUMNHEADERS	= 0x00000004,	// enable column headers (LT_TABLE only)
		LP_CANSORT			= 0x00000008,	// enable sorting
		LP_MANUALCOLUMNSIZE	= 0x00000010,	// enable fixed column size (LT_TABLE only)
		LP_MANUALROWSIZE	= 0x00000020,	// enable fixed row size
		LP_SELECTBYCOLUMN	= 0x00000040,	// NOT IMPLEMENTED // select individual items instead of entire row (LT_TABLE only)
		LP_CANEDIT			= 0x00000080,	// NOT IMPLEMENTED // text entries are editable
		LP_DISABLETEXT		= 0x00000100,	// do not show text (LT_GRID only)
		LP_MULTISELECT		= 0x00000200,	// enable multiple selection of items
		LP_VARIABLE_HEIGHT	= 0x00000400,	// enabled each row to have variable height based on content (LT_TABLE only)

		LP_DEFAULTS			= LP_CANVSCROLL | LP_CANSORT,
	};

	UIListWidget(UIWidget* pParent, const std::string& Name = "", LIST_TYPE ListType = LT_TABLE, unsigned long Flags = LP_DEFAULTS);
	virtual ~UIListWidget();

	/// load from table
	virtual bool		Create(const std::string& Name, const math::Vec2& Pos = math::Vec2(0.5f, 0.5f), const math::Vec2& Size= math::Vec2(0.1f, 0.1f));
	virtual	bool		Load(LuaPlus::LuaObject& _LObj);
	/// update
	virtual	bool		Tick(float dt);
	/// display
	virtual bool		Render(bool Force = false);
	/// register with LUA Bind
	static	void		Register(LuaPlus::LuaState* _pScript);

	/// specifics for list widget
	virtual bool		OnMouseMove(const math::Vec2& From, const math::Vec2& To);
	virtual bool		OnMousePress(const math::Vec2& Pos, unsigned int Button);
	virtual bool		OnMouseDblClick(const math::Vec2& Pos, unsigned int Button);
	virtual bool		OnMouseRelease(const math::Vec2& Pos, unsigned int Button);
	virtual bool		OnKeyEvent(const input::Keyboard::KEY& Key);

	virtual void		RefreshText();
	virtual UIWidget*	GetClone();
	virtual void		UpdateLocalization();
	virtual void		RefreshFonts();

	void				SetColumnFont(const std::string& Font);
	void				SetDataFont(const std::string& Font);
	void				SetColumnColor(COLOR c, bool ForceReset = false);
	void				SetDataColor(COLOR c, bool ForceReset = false);
	void				SetAlignment(UIText::TEXT_JUST_HORIZ HJust, UIText::TEXT_JUST_VERT VJust, int Column = 0);
	void				SetCellAlignment(UIText::TEXT_JUST_HORIZ HJust, UIText::TEXT_JUST_VERT VJust, int Row, int Column);

	void				SetItemTextA(const std::string& Text, int Row, int Column = 0);
	void				SetItemTextW(const std::wstring& Text, int Row, int Column = 0);
	std::string			GetItemTextA(int Row, int Column = 0);
	std::wstring		GetItemTextW(int Row, int Column = 0);
	void				SetItemIcon(const std::string& Icon, const math::Vec2& Size, int Row, int Column = 0);
	std::string			GetItemIcon(int Row, int Column = 0);
	UISprite*			GetItemSprite(int Row, int Column = 0);
	void				SetItemData(unsigned long Data, int Row, int Column = 0);
	unsigned long		GetItemData(int Row, int Column = 0);
	void				SetItemPointer(void* pvData, int Row, int Column = 0);
	void*				GetItemPointer(int Row, int Column = 0);
	void				SetItemColor(COLOR c, int Row, int Column = 0, bool AllowGetHeader = false);
	COLOR				GetItemColor(int Row, int Column = 0, bool AllowGetHeader = false);
	void				SetRowColor(COLOR c, int Row, bool AllowGetHeader = false);
	ListItem*			GetItem(int Row, int Column = -1, bool AllowGetHeader = false);
	ListItem*			GetItem(const math::Vec2& Pos, bool AllowGetHeader = false);
	bool				FindItem(const std::string& Text, int* pRow, int* pCol = NULL);
	bool				FindItem(const std::wstring& Text, int* pRow,int* pCol = NULL);
	bool				FindItem(unsigned long Data, int* pRow, int* pCol = NULL);
	bool				FindItem(void* pvData, int* pRow, int* pCol = NULL);
	void				SetItemSelected(int Row, int Column = 0, bool SetSelected = true);
	int					GetAllSelectedItems(std::vector<ListItem*>& Set);
	int					GetAllSelectedItems(std::vector<int>& Set);
	void				ClearAllSelectedItems();
	int					InsertItem(const std::string& Text, int Row = -1);
	int					InsertItem(const std::wstring& Text, int Row = -1);
	int					InsertItem(const std::wstring& Text, const std::string& Icon, const math::Vec2& IconSize = math::Vec2(0,0), int Row = -1);
	void				DeleteItem(int Row);
	void				DeleteAllItems();
	bool				AddColumnA(const std::string& Text = "");
	bool				AddColumnW(const std::wstring& Text = L"");
	std::string			GetColumnTextA(int Column);
	std::wstring		GetColumnTextW(int Column);
	void				SetColumnTextA(const std::string& Text, int Column);
	void				SetColumnTextW(const std::wstring& Text, int Column);
	int					GetItemC();
	int					GetRowC();
	int					GetColumnC();
	int					GetMaxVisibleRows(int StartRow = 0);
	int					GetMaxVisibleCols(int StartCol = 0);
	int					GetHScroll();
	void				SetHScroll(int Val, bool bAbsolute = false);
	int					GetVScroll();
	void				SetVScroll(int Val, bool bAbsolute = false);
	int					SetVScrollToEnd();
	void				ClearList();
	void				ReSortList(bool ForceRefresh = true);

	void				SetListProperty(LIST_PROPERTIES Property);
	void				RemoveListProperty(LIST_PROPERTIES Property);
	bool				GetListProperty(LIST_PROPERTIES Property);
	void				ResetListProperty(unsigned long Properties = LP_DEFAULTS);

	void				SetRowHeight(float f);
	float				GetRowHeight(int Row = 0, bool AllowGetHeader = false);
	void				SetColumnWidth(float f, int Col = 0);
	float				GetColumnWidth(int Col = 0);
	void				SetRowSpacing(float f)		{ m_RowSpacing = f; m_CacheDirty = true; }
	float				GetRowSpacing() const		{ return m_RowSpacing; }
	void				SetColumnSpacing(float f)	{ m_ColumnSpacing = f; m_CacheDirty = true; }
	float				GetColumnSpacing() const	{ return m_ColumnSpacing; }
	ListItem*			GetHoverItem()				{ return m_pHover; }
	void				SetSliderH(UISlider* pUI)	{ m_pSliderH = pUI; }
	void				SetSliderV(UIVerSlider* pUI)	{ m_pSliderV = pUI; }
	void				ResetSliders()				{ m_SliderCacheH = m_SliderCacheV = -1.0f; }

	/// callbacks
	typedef int			(*ListSortCB)(ListItem* pItem1, ListItem* pItem2, UIListWidget* pWidget, void* pvData);								// listwidget sorting callback
	typedef void		(*ListSelectCB)(ListItem* pItem, bool IsSelected, UIListWidget* pWidget, void* pvData);								// on select item callback
	typedef void		(*ListRenderCB)(ListItem* pItem, const math::Vec2& From, const math::Vec2& To, UIListWidget* pWidget, void* pvData);// on render item callback
	typedef void		(*ListEditCB)(ListItem* pItem, UIListWidget* pWidget, void* pvData);												// NOT IMPLEMENTED // on item edited

	void				SetSortCB(ListSortCB pFunc = NULL, void* pvData = NULL)			{ m_pSortFunc = pFunc; m_pvSortCBData = pvData; }
	void				SetSelectCB(ListSelectCB pFunc = NULL, void* pvData = NULL)		{ m_pSelectFunc = pFunc; m_pvSelectCBData = pvData; }
	void				SetPreRenderCB(ListRenderCB pFunc = NULL, void* pvData = NULL)	{ m_pPreRenderFunc = pFunc; m_pvPreRenderCBData = pvData; }
	void				SetPostRenderCB(ListRenderCB pFunc = NULL, void* pvData = NULL)	{ m_pPostRenderFunc = pFunc; m_pvPostRenderCBData = pvData; }
	void				SetEditCB(ListEditCB pFunc = NULL, void* pvData = NULL)			{ m_pEditFunc = pFunc; m_pvEditCBData = pvData; }

	// callbacks using functors
	typedef util::Delegate3<ListItem*, bool, UIListWidget*>									tdgListSelect;
	typedef util::Delegate4<ListItem*, const math::Vec2&, const math::Vec2&, UIListWidget*>	tdgListRender;
	typedef util::Delegate2<ListItem*,UIListWidget*>										tdgListEdit;

	void				SetSelectDg(const tdgListSelect& pFunc = NULL)		{ m_pSelectFuncDg = pFunc; }
	void				SetPreRenderDg(const tdgListRender& pFunc = NULL)	{ m_pPreRenderFuncDg = pFunc; }
	void				SetPostRenderDg(const tdgListRender& pFunc = NULL)	{ m_pPostRenderFuncDg = pFunc; }
	void				SetEditDg(const tdgListEdit& pFunc = NULL)			{ m_pEditFuncDg = pFunc; }



	// static string mapping
	static std::string	MapListToString(LIST_TYPE t);
	static LIST_TYPE	MapStringToList(const std::string& t);

	// luabind
	void				SetAlignmentStr(const std::string& HJust, const std::string& VJust, int Column);
	void				SetCellAlignStr(const std::string& HJust, const std::string& VJust, int Row, int Column);
	int					InsertItemStr(const std::string& Text, int Row);
	// should register script functions for table properties?

protected:
	/// export widget specifics to file
	virtual bool		ExportSpecificsToFile(script::WriteLua*	pWL);
	void				RecreateList(bool bForce = false);
	void				CalcCellDim(ListItem* pItem, float& MaxW, float& MaxH);
	inline void			RenderItem(ListItem* pItem, const math::Vec2& Pos, const math::Vec2& Size, const int row, const int col);
	void				InitTextObj(ListItem* pItem, int Col);
	float				CalcTotalRowH(int From = 0, int To = -1);
	float				CalcTotalColumnW(int From = 0, int To = -1);

	bool				IsGrid() const	{ return m_ListType == LT_GRID; }
	bool				IsTable() const	{ return m_ListType == LT_TABLE; }

	LIST_TYPE			m_ListType;
	std::string			m_ColumnFont;
	std::string			m_DataFont;
	COLOR				m_ColumnColor;
	COLOR				m_DataColor;

	struct COLUMN_SPECIFICS
	{
		float					Width;
		UIText::TEXT_JUST_HORIZ	DefaultHJust;
		UIText::TEXT_JUST_VERT	DefaultVJust;
		COLUMN_SPECIFICS()
		{
			Width = 0.0f;
			DefaultHJust = UIText::TJH_CENTER;
			DefaultVJust = UIText::TJV_CENTER;
		}
	};
	std::vector<COLUMN_SPECIFICS>	m_ColumnSpecs;

	float				m_ColumnSpacing;
	float				m_RowSpacing;
	float				m_RowHeight;
	unsigned long		m_Properties;
	unsigned int		m_Width;
	unsigned int		m_Height;
	int					m_CurrentRow;
	int					m_CurrentCol;
	std::vector<ListData*>	m_ListItems;
	ListItem*			m_pHover;

	UISlider*			m_pSliderH;
	UIVerSlider*		m_pSliderV;

	// callback data
	ListSortCB			m_pSortFunc;
	ListSelectCB		m_pSelectFunc;
	ListRenderCB		m_pPreRenderFunc;
	ListRenderCB		m_pPostRenderFunc;
	ListEditCB			m_pEditFunc;
	void*				m_pvSortCBData;
	void*				m_pvSelectCBData;
	void*				m_pvPreRenderCBData;
	void*				m_pvPostRenderCBData;
	void*				m_pvEditCBData;

	tdgListSelect		m_pSelectFuncDg;
	tdgListRender		m_pPreRenderFuncDg;
	tdgListRender		m_pPostRenderFuncDg;
	tdgListEdit			m_pEditFuncDg;

private:
	bool				m_CacheDirty;
	float				m_SliderCacheH;
	float				m_SliderCacheV;

	// delay loading certain elements during UI load, as dependent items may not be loaded yet
	struct DELAYED_LOAD
	{
		bool			HasData;
		std::string		LoadHSlider;
		std::string		LoadVSlider;
		DELAYED_LOAD()
		{
			HasData = false;
		}
	};
	DELAYED_LOAD		m_DelayedLoad;

	// default callbacks if none are specified
	static int			DefaultSortFuncCB(ListItem* pItem1, ListItem* pItem2, UIListWidget* pWidget, void* pvData);
};

class ListItem
{
public:
	friend class		UIListWidget;
	ListItem(ListData* pParent)
	{
		m_pParent = pParent;
		m_Data.dwData = 0;
		m_Width = 0.0f;
		m_CustomHJust = -1;
		m_CustomVJust = -1;
		m_TextColor = -1;
	}
	~ListItem() {}
	ListData*	GetParent()		{ return m_pParent ? m_pParent : (ListData*)this; }

	std::wstring		m_Text;
	std::string			m_Icon;
	union ItemData
	{
		unsigned long	dwData;
		void*			pvData;
	};
	ItemData			m_Data;
	int					m_CustomHJust;
	int					m_CustomVJust;
	COLOR				m_TextColor;

protected:
	ListData*			m_pParent;
	UISprite			m_Sprite;
	UIText				m_TextObj;
	float				m_Width;
};

class ListData : public ListItem
{
public:
	friend class		UIListWidget;
	ListData() : ListItem(NULL)
	{
		m_IsColumnHeader = false;
		m_SelectedIdx = -1;
		m_Height = 0.0f;
	}
	~ListData() {}
	bool	IsSelected()	{ return m_SelectedIdx >= 0; }

	bool				m_IsColumnHeader;
	int					m_SelectedIdx;
	std::vector<ListItem>	m_ColumnData;

protected:
	float				m_Height;
};

} //namespce ui

#endif //_UI_LISTWIDGET_H_
